package com.bsdsma.visonManagementSystemapp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsdsma.visonManagementSystemapp.constants.ErrorCodes;
import com.bsdsma.visonManagementSystemapp.dto.*;
import com.bsdsma.visonManagementSystemapp.entity.Baogao;
import com.bsdsma.visonManagementSystemapp.entity.School;
import com.bsdsma.visonManagementSystemapp.entity.User;
import com.bsdsma.visonManagementSystemapp.entity.Student;
import com.bsdsma.visonManagementSystemapp.entity.Teacher;
import com.bsdsma.visonManagementSystemapp.enums.AlertLevel;
import com.bsdsma.visonManagementSystemapp.exceptionhandler.GlobalRuntimeException;
import com.bsdsma.visonManagementSystemapp.mapper.BaogaoMapper;
import com.bsdsma.visonManagementSystemapp.mapper.StudentMapper;
import com.bsdsma.visonManagementSystemapp.service.BaogaoService;
import com.bsdsma.visonManagementSystemapp.service.SchoolService;
import com.bsdsma.visonManagementSystemapp.service.UserService;
import com.bsdsma.visonManagementSystemapp.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class BaogaoServiceImpl implements BaogaoService {

    /**
     * 使用Spring注入Mybatis操作对象
     */
    @Autowired
    private BaogaoMapper baogaoMapper;
    @Lazy
    @Autowired
    private UserService userService;
    @Lazy
    @Autowired
    private StudentMapper studentMapper;
    @Lazy
    @Autowired
    private SchoolService schoolService;

    private int getCurrentYear() {
        return Calendar.getInstance().get(Calendar.YEAR);
    }

    @Override
    public PageRetDto<BaogaoWithUserDto> search(BaogaoSearchDto dto) {
        // Get current user from LocalUtils
        User currentUser = LocalUtils.getUser();
        if (currentUser == null) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "请先登录");
        }

        // Build query for reports
        QueryWrapper<Baogao> reportWrapper = new QueryWrapper<>();

        // Add common search conditions
        if (dto.getYear() != null) {
            reportWrapper.eq("b.year", dto.getYear());
        }
        if (StringUtils.isNotBlank(dto.getAlert())) {
            reportWrapper.eq("b.alert", dto.getAlert());
        }
        if (StringUtils.isNotBlank(dto.getName())) {
            reportWrapper.like("s.name", dto.getName());
        }

        if (currentUser instanceof Student) {
            // If current user is a student, only show their own reports
            reportWrapper.eq("b.xuehao", ((Student) currentUser).getXuehao());
        } else if (currentUser instanceof Teacher) {
            // If current user is a teacher, only show reports for students from the same school
            final long xuexiao_id = ((Teacher) currentUser).getXuexiao().getId();
            
            // First get all students' xuehao values from the same school
            List<Student> students = userService.listAllStudentsBySchool(xuexiao_id);
            
            if (CollectionUtils.isEmpty(students)) {
                return new PageRetDto<>();
            }

            // Extract xuehao values from students
            List<Long> studentXuehaoList = students.stream()
                    .map(Student::getXuehao)
                    .filter(xuehao -> xuehao != null && xuehao != 0)
                    .collect(Collectors.toList());

            if (CollectionUtils.isEmpty(studentXuehaoList)) {
                return new PageRetDto<>();
            }

            // Add xuehao filter for students
            reportWrapper.in("b.xuehao", studentXuehaoList);

            // If xuehao is provided, search directly by xuehao
            if (dto.getXuehao() != null) {
                reportWrapper.eq("b.xuehao", dto.getXuehao());
            } else {
                // Otherwise, search by user criteria
                List<Student> matchingStudents = userService.searchStudents(
                    xuexiao_id,  // school
                    dto.getNianji(),  // grade
                    dto.getName()  // name
                );
                
                if (CollectionUtils.isEmpty(matchingStudents)) {
                    return new PageRetDto<>();
                }

                // Extract xuehao values from matching students
                List<Long> xuehaoList = matchingStudents.stream()
                        .map(Student::getXuehao)
                        .filter(xuehao -> xuehao != null && xuehao != 0)
                        .collect(Collectors.toList());

                if (CollectionUtils.isEmpty(xuehaoList)) {
                    return new PageRetDto<>();
                }

                // Add xuehao filter using the list of xuehao values
                reportWrapper.in("b.xuehao", xuehaoList);
            }
        } else {
            // For admin role, use normal search logic
            if (dto.getXuehao() != null) {
                reportWrapper.eq("b.xuehao", dto.getXuehao());
            } else {
                // Otherwise, search by user criteria
                List<Student> matchingStudents;
                if (StringUtils.isBlank(dto.getXuexiao())) {
                    // If no school specified, search all schools
                    matchingStudents = userService.searchStudents(
                        null,  // school
                        dto.getNianji(),  // grade
                        dto.getName()  // name
                    );
                } else {
                    // Search in specific school
                    School school = schoolService.findByName(dto.getXuexiao());
                    if (school == null) {
                        return new PageRetDto<>();
                    }
                    matchingStudents = userService.searchStudents(
                        school.getId(),  // school
                        dto.getNianji(),  // grade
                        dto.getName()  // name
                    );
                }
                
                if (CollectionUtils.isEmpty(matchingStudents)) {
                    return new PageRetDto<>();
                }

                // Extract xuehao values from matching students
                List<Long> xuehaoList = matchingStudents.stream()
                        .map(Student::getXuehao)
                        .filter(xuehao -> xuehao != null && xuehao != 0)
                        .collect(Collectors.toList());

                if (CollectionUtils.isEmpty(xuehaoList)) {
                    return new PageRetDto<>();
                }

                // Add xuehao filter using the list of xuehao values
                reportWrapper.in("b.xuehao", xuehaoList);
            }
        }

        // Execute the report query with pagination and join with user table
        Page<BaogaoWithUserDto> page = new Page<>(dto.getPage(), dto.getSize());
        IPage<BaogaoWithUserDto> pageData = baogaoMapper.selectPageWithUser(page, reportWrapper);
        
        return PageRetUtils.parse(pageData);
    }

    @Override
    public List<Baogao> listAll() {
        return this.listAllWithRet().getList();
    }

    @Override
    public ListAllRetDto<Baogao> listAllWithRet() {
        // Get current user from LocalUtils
        User currentUser = LocalUtils.getUser();
        if (currentUser == null) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "请先登录");
        }

        QueryWrapper<Baogao> wrapper = new QueryWrapper<>();
        boolean onlyMine = false;

        if (currentUser instanceof Student) {
            // Students can only see their own reports
            wrapper.eq("b.xuehao", ((Student) currentUser).getXuehao());
            onlyMine = true;
        } else if (currentUser instanceof Teacher) {
            // Teachers can only see reports from their school
            final long xuexiao_id = ((Teacher) currentUser).getXuexiao().getId();
            
            // Get all students from the same school
            List<Student> students = userService.listAllStudentsBySchool(xuexiao_id);
            
            if (!CollectionUtils.isEmpty(students)) {
                List<Long> studentXuehaoList = students.stream()
                        .map(Student::getXuehao)
                        .filter(xuehao -> xuehao != null && xuehao != 0)
                        .collect(Collectors.toList());
                
                if (!CollectionUtils.isEmpty(studentXuehaoList)) {
                    wrapper.in("b.xuehao", studentXuehaoList);
                }
            }
        }
        // For admin role, no additional filtering needed

        return new ListAllRetDto<>(onlyMine, baogaoMapper.selectList(wrapper));
    }

    @Override
    public BaogaoDto add(BaogaoAddDto dto) {
        // Get current user from LocalUtils
        User currentUser = LocalUtils.getUser();
        if (currentUser == null) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "请先登录");
        }

        // Convert to UserDto for role check
        UserDto currentUserDto = userService.getById(currentUser.getRole(), currentUser.getId());
        if (currentUserDto == null) {
            throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无效的用户");
        }

        // Validate student exists
        Student student = studentMapper.selectOne(new QueryWrapper<Student>()
            .eq("xuehao", dto.getXuehao())
            .eq("status", 0));
        if (student == null) {
            throw new GlobalRuntimeException(ErrorCodes.OBJ_NOT_EXISTS, "学生不存在");
        }

        // Role-based access control
        switch (currentUserDto.getRole()) {
            case "教师":
                // Teachers can only add reports for students from their school
                if (!currentUserDto.getXuexiao().equals(student.getXuexiao())) {
                    throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无权为其他学校的学生添加报告");
                }
                // Teachers can only add current year's report
                if (dto.getYear() != getCurrentYear()) {
                    throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "只能添加当前年份的报告");
                }
                // Check if report already exists for this year
                if (existsByXuehaoAndYear(dto.getXuehao(), dto.getYear())) {
                    throw new GlobalRuntimeException(ErrorCodes.OBJ_ALREADY_EXISTS, "该学生本年度已有报告");
                }
                break;

            case "超级管理员":
                // Admin can add any report
                break;

            default:
                throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无权添加报告");
        }

        // Validate alert value
        try {
            AlertLevel.fromString(dto.getAlert());
        } catch (IllegalArgumentException e) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "无效的预警级别");
        }

        // Create new report
        Baogao baogao = new Baogao();
        baogao.setXuehao(dto.getXuehao());
        baogao.setLeftEye(dto.getLeftEye());
        baogao.setRightEye(dto.getRightEye());
        baogao.setConclusion(dto.getConclusion());
        baogao.setRecommend(dto.getRecommend());
        baogao.setYear(dto.getYear());
        baogao.setAlert(dto.getAlert());
        baogao.setCt(DateUtils.getNow());
        baogao.setUt(DateUtils.getNow());

        baogaoMapper.insert(baogao);
        return convertToDto(baogao);
    }

    @Override
    public BaogaoDto update(Long id, BaogaoAddDto dto) {
        // Get current user from LocalUtils
        User currentUser = LocalUtils.getUser();
        if (currentUser == null) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "请先登录");
        }

        // Students cannot update reports
        if (currentUser instanceof Student) {
            throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "学生没有修改视力报告的权限");
        }

        // Validate xuehao exists and user is not deleted
        Student student = userService.getStudentByXuehaoAndStatus(dto.getXuehao(), 0);
        if (student == null) {
            throw new GlobalRuntimeException(ErrorCodes.USER_NOT_FOUND, "学号 " + dto.getXuehao() + " 没有找到");
        }

        // Get the existing report
        Baogao bean = baogaoMapper.selectById(id);
        if (bean == null) {
            throw new GlobalRuntimeException(ErrorCodes.OBJ_NOT_EXISTS, "视力报告不存在");
        }

        // For teachers, validate that the student belongs to their school and report is from current year
        if (currentUser instanceof Teacher) {
            final long xuexiao_id = ((Teacher) currentUser).getXuexiao().getId();
            if (xuexiao_id != student.getXuexiao().getId()) {
                throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "只能修改本校学生的视力报告");
            }
            // Check if report is from current year
            if (bean.getYear() != getCurrentYear()) {
                throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "只能修改当前年份的报告");
            }
            // Validate that the update is also for current year
            if (dto.getYear() != getCurrentYear()) {
                throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "只能修改为当前年份的报告");
            }
        }

        // Validate alert value
        try {
            AlertLevel.fromString(dto.getAlert());
        } catch (IllegalArgumentException e) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "无效的预警级别");
        }

        // Update the report
        bean.setLeftEye(dto.getLeftEye());
        bean.setRightEye(dto.getRightEye());
        bean.setConclusion(dto.getConclusion());
        bean.setRecommend(dto.getRecommend());
        bean.setYear(dto.getYear());
        bean.setAlert(dto.getAlert());
        bean.setUt(DateUtils.getNow());
        baogaoMapper.updateById(bean);
        return convertToDto(bean);
    }

    @Override
    public void delete(Long id) {
        // Get current user from LocalUtils
        User currentUser = LocalUtils.getUser();
        if (currentUser == null) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "请先登录");
        }

        // Students cannot delete reports
        if (currentUser instanceof Student) {
            throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "学生没有删除视力报告的权限");
        }

        // Get the existing report
        Baogao bean = baogaoMapper.selectById(id);
        if (bean == null) {
            throw new GlobalRuntimeException(ErrorCodes.OBJ_NOT_EXISTS, "视力报告不存在");
        }

        // For teachers, validate that the student belongs to their school and report is from current year
        if (currentUser instanceof Teacher) {
            // Get the student's information
            Student student = userService.getStudentByXuehaoAndStatus(bean.getXuehao(), 0);
            
            if (student == null) {
                throw new GlobalRuntimeException(ErrorCodes.USER_NOT_FOUND, "找不到该学生信息");
            }

            final long xuexiao_id = ((Teacher) currentUser).getXuexiao().getId();
            if (xuexiao_id != student.getXuexiao().getId()) {
                throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "只能删除本校学生的视力报告");
            }
            // Check if report is from current year
            if (bean.getYear() != getCurrentYear()) {
                throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "只能删除当前年份的报告");
            }
        }

        // Delete the report (hard delete)
        baogaoMapper.deleteById(id);
    }

    @Override
    public Baogao getById(Long id) {
        // Get current user from LocalUtils
        User currentUser = LocalUtils.getUser();
        if (currentUser == null) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "请先登录");
        }

        Baogao bean = baogaoMapper.selectById(id);
        if (bean == null) {
            throw new GlobalRuntimeException(ErrorCodes.OBJ_NOT_EXISTS, "视力报告不存在");
        }

        // For students, only allow viewing their own reports
        if (currentUser instanceof Student) {
            if (!((Student) currentUser).getXuehao().equals(bean.getXuehao())) {
                throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "只能查看自己的视力报告");
            }
        }
        // For teachers, only allow viewing reports from their school
        else if (currentUser instanceof Teacher) {
            // Get the student's information
            Student student = userService.getStudentByXuehaoAndStatus(bean.getXuehao(), 0);
            
            if (student == null) {
                throw new GlobalRuntimeException(ErrorCodes.USER_NOT_FOUND, "找不到该学生信息");
            }

            final long xuexiao_id = ((Teacher) currentUser).getXuexiao().getId();
            if (xuexiao_id != student.getXuexiao().getId()) {
                throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "只能查看本校学生的视力报告");
            }
        }
        // For admin role, no additional checks needed

        return bean;
    }

    @Override
    public int updateById(Baogao bean) {
        return baogaoMapper.updateById(bean);
    }

    @Override
    public Long count() {
        return baogaoMapper.selectCount(new QueryWrapper<>());
    }

    @Override
    public List<DataGroupByDateDto> countByDay(Date beginDate, Date endDate) {
        return baogaoMapper.countByDay(beginDate, endDate);
    }

    @Override
    public List<BaogaoDto> getByXuehao(Long xuehao) {
        // Get current user
        User currentUser = LocalUtils.getUser();
        if (currentUser == null) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "请先登录");
        }

        // Convert to UserDto for role check
        UserDto currentUserDto = userService.getById(currentUser.getRole(), currentUser.getId());
        if (currentUserDto == null) {
            throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无效的用户");
        }

        // Check if student exists
        Student student = studentMapper.selectOne(new QueryWrapper<Student>()
            .eq("xuehao", xuehao)
            .eq("status", 0));
        if (student == null) {
            throw new GlobalRuntimeException(ErrorCodes.OBJ_NOT_EXISTS, "学生不存在");
        }

        // Role-based access control
        switch (currentUserDto.getRole()) {
            case "学生":
                // Students can only view their own reports
                if (!currentUserDto.getXuehao().equals(xuehao)) {
                    throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无权查看其他学生的报告");
                }
                break;

            case "教师":
                // Teachers can only view reports of students from their school
                if (!currentUserDto.getXuexiao().equals(student.getXuexiao())) {
                    throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无权查看其他学校学生的报告");
                }
                break;

            case "超级管理员":
                // Admin can view all reports
                break;

            default:
                throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无效的用户角色");
        }

        // Get all reports for the student
        List<Baogao> reports = baogaoMapper.selectList(new QueryWrapper<Baogao>()
            .eq("xuehao", xuehao)
            .orderByDesc("year"));

        return reports.stream()
            .map(this::convertToDto)
            .collect(Collectors.toList());
    }

    @Override
    public List<BaogaoDto> getBySchool(String school, Integer year) {
        // Get current user
        User currentUser = LocalUtils.getUser();
        if (currentUser == null) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "请先登录");
        }

        // Convert to UserDto for role check
        UserDto currentUserDto = userService.getById(currentUser.getRole(), currentUser.getId());
        if (currentUserDto == null) {
            throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无效的用户");
        }

        // Role-based access control
        switch (currentUserDto.getRole()) {
            case "教师":
                // Teachers can only view reports from their school
                if (!currentUserDto.getXuexiao().equals(school)) {
                    throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无权查看其他学校的报告");
                }
                break;

            case "超级管理员":
                // Admin can view all reports
                break;

            default:
                throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无权查看学校报告");
        }

        // Build query
        QueryWrapper<Baogao> wrapper = new QueryWrapper<Baogao>()
            .inSql("xuehao", "SELECT xuehao FROM student WHERE xuexiao = '" + school + "' AND status = 0")
            .orderByDesc("year", "xuehao");

        if (year != null) {
            wrapper.eq("year", year);
        }

        List<Baogao> reports = baogaoMapper.selectList(wrapper);
        return reports.stream()
            .map(this::convertToDto)
            .collect(Collectors.toList());
    }

    @Override
    public List<BaogaoDto> getAll(Integer year) {
        // Get current user
        User currentUser = LocalUtils.getUser();
        if (currentUser == null) {
            throw new GlobalRuntimeException(ErrorCodes.PARAMS_ERROR, "请先登录");
        }

        // Convert to UserDto for role check
        UserDto currentUserDto = userService.getById(currentUser.getRole(), currentUser.getId());
        if (currentUserDto == null) {
            throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无效的用户");
        }

        // Only admin can view all reports
        if (!"超级管理员".equals(currentUserDto.getRole())) {
            throw new GlobalRuntimeException(ErrorCodes.PERMISSION_ERROR, "无权查看所有报告");
        }

        // Build query
        QueryWrapper<Baogao> wrapper = new QueryWrapper<Baogao>()
            .orderByDesc("year", "xuehao");

        if (year != null) {
            wrapper.eq("year", year);
        }

        List<Baogao> reports = baogaoMapper.selectList(wrapper);
        return reports.stream()
            .map(this::convertToDto)
            .collect(Collectors.toList());
    }

    @Override
    public BaogaoDto getCurrentYearReport(Long xuehao) {
        return getByXuehao(xuehao).stream()
            .filter(report -> report.getYear() == getCurrentYear())
            .findFirst()
            .orElse(null);
    }

    @Override
    public boolean existsByXuehaoAndYear(Long xuehao, Integer year) {
        return baogaoMapper.selectCount(new QueryWrapper<Baogao>()
            .eq("xuehao", xuehao)
            .eq("year", year)) > 0;
    }

    private BaogaoDto convertToDto(Baogao baogao) {
        BaogaoDto dto = new BaogaoDto();
        dto.setId(baogao.getId());
        dto.setXuehao(baogao.getXuehao());
        dto.setLeftEye(baogao.getLeftEye());
        dto.setRightEye(baogao.getRightEye());
        dto.setConclusion(baogao.getConclusion());
        dto.setRecommend(baogao.getRecommend());
        dto.setYear(baogao.getYear());
        dto.setAlert(baogao.getAlert());
        dto.setCt(baogao.getCt());
        dto.setUt(baogao.getUt());

        // Get student info if available
        if (baogao.getStudent() != null) {
            dto.setStudentName(baogao.getStudent().getName());
            dto.setXuexiao(baogao.getStudent().getXuexiao().getName());
        } else {
            // If student info is not loaded, fetch it with school info
            Student student = studentMapper.selectByIdWithSchool(
                    studentMapper.selectOne(new QueryWrapper<Student>()
                            .eq("xuehao", baogao.getXuehao())
                            .eq("status", 0)).getId()
            );
            if (student != null) {
                dto.setStudentName(student.getName());
                dto.setXuexiao(student.getXuexiao().getName());
            }
        }

        return dto;
    }
}
